<?php
/**
 * Created by PhpStorm.
 * User: qch
 * Date: 2015/9/13
 * Time: 17:31
 */

namespace J\Util;


class Tree
{
    protected $nodes;

    protected $list = [];

    protected $key ;

    /**
     * Tree constructor.
     *
     * @param string $key
     */
    public function __construct($key = 'id')
    {
        $this->key  = $key;
    }


    public function fromList($list, $pKey)
    {
        $this->nodes = [];
        $this->nodes[0] = new Node($this,null,0);

        if(is_array($list)) {
            foreach ($list as $key => $data) {
                $this->list[$this->getKey($data,$key)] = $data;
            }
            foreach ($this->list as $key => $data) {
                if(!isset($this->nodes[$key])){
                    $this->createNode($key,$pKey);
                }
            }
        }

        return $this;
    }

    protected function createNode($id, $pid)
    {
        $parentId = (int)($this->list[$id][$pid]);
        if(isset($this->nodes[$parentId])){ // 父节点已建立
            $this->nodes[$id] = new Node($this,$this->nodes[$parentId],$id);
        }elseif(isset($this->list[$parentId])){ // 父节点存在
            $this->nodes[$id] = new Node($this,$this->createNode($parentId,$pid),$id);
        }else{ // 不存在父节点
            $this->nodes[$id] = new Node($this,$this->nodes[0],$id);
        }
        return $this->nodes[$id];
    }

    public function getNode($id)
    {
        return isset($this->nodes[$id])?$this->nodes[$id]:null;
    }

    /**
     * @return Node
     */
    public function getRoot()
    {
        return $this->getNode(0);
    }

    public function getNodeValue($id)
    {
        return array_isset($this->list,$id,[]);
    }

    protected function getKey($value,$key)
    {
        if($this->key){
            return ((int)($value[$this->key]));
        }else{
            return ((int)$key);
        }
    }

    public function setNodeValue($id,$value,$key=null)
    {
        if($key === null){
            $this->list[$id] = $value;
        }else{
            $this->list[$id][$key] = $value;
        }
    }
}


class Node
{
    /**
     * @var Node 父节点
     */
    protected $parent = null;

    /**
     * @var int ID
     */
    protected $id;

    /**
     *   id=>Node
     * @var Node[] 子节点列表
     */
    protected $children = [];

    /**
     * @var Tree
     */
    protected $tree;

    /**
     * Node constructor.
     * @param $tree
     * @param Node|null $parent
     * @param int $id Id
     */
    public function __construct($tree,$parent, $id)
    {
        $this->tree = $tree;
        $this->parent = $parent;
        $this->id = $id;
        if($parent){
            $parent->addChild($this);
        }
    }

    /**
     * 存取操作
     * @param string $key
     * @param mixed $value
     * @return mixed
     */
    public function update($key = null, $value = null)
    {
        $v =  $this->getValue();
        // 获取节点全部信息
        if($key === null) return $v;
        // 获取节点指定键
        if($value === null) return array_isset($v, $key);

        // 设置Node值
        $this->tree->setNodeValue($this->getId(), $value, $key);

        return $value;
    }

    public function getValue()
    {
        return $this->tree->getNodeValue($this->getId());
    }

    /**
     * 父节点
     * @return Node
     */
    public function getParent()
    {
        return $this->parent;
    }

    /**
     * 祖宗节点列表
     * @return Node[]
     */
    public function getParents()
    {
        $parents = $this->parent->getParents();
        $parents[$this->parent->getId()] = $this->parent;
        return $parents;
    }

    /**
     * @return int
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * 添加子节点
     * @param Node $child
     */
    public function addChild(Node $child)
    {
        $this->children[$child->getId()] = $child;
    }

    /**
     * 返回全部子节点
     * @return Node[]
     */
    public function getChildren()
    {
        $return = $this->children;
        foreach($this->children as $id=>$child){
            $return = array_merge($return,$child->getChildren());
        }
        return $return;
    }

    /**
     * 是否包含直接子节点(null返回全部直接子节点)
     * @param int $child
     * @return Node[]|Node|false
     */
    public function getChild($child = null)
    {
        if($child === null){
            return array_values($this->children);
        }
        return array_isset($this->children,$child,[]);
    }

    /**
     * 路径前有 '/'
     *
     * @param string $path
     * @return string
     */
    public function getPath($path='path')
    {
        $p = $this->getParent();
        if($p and !$p->isRoot()){

            return $p->getPath($path).'/'.$this->update($path);
        }
        // 没有路径，直接跳过
        if(empty($this->update($path))) return '';

        return '/'.$this->update($path);
    }

    public function isRoot()
    {
        return ($this->id == 0);
    }

    /**
     * 自定义树
     *
     * @param \Closure $func 方法
     * @param string $children
     * @return array
     */
    public function map($func, $children = null)
    {
        $row = [];
        // 跳过根节点
        if(!$this->isRoot()) $row = $func($this);

        if ($children === null) return $row;

        foreach ($this->getChild() as $node)
            $row[$children][] = $node->map($func, $children);

        return $row;
    }


}